import { PropsWithChildren, useEffect, useMemo, useRef, useState } from "react";
import { useEventListener, useMouse } from "ahooks";
import { io } from "socket.io-client";
import { EMIT_INIT } from "../../constrants";
import { Switch } from "antd";

interface IProps {
  className?: string;
}

function ArrayBufferToBase64(buffer: ArrayBuffer) {
  //第一步，将ArrayBuffer转为二进制字符串
  var binary = "";
  var bytes = new Uint8Array(buffer);
  for (var len = bytes.byteLength, i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i]);
  }
  //将二进制字符串转为base64字符串
  return window.btoa(binary);
}

let tmpClients: any[] = [];

function loopCall(fn: any) {
  requestIdleCallback(() => {
    fn();
    loopCall(fn);
  });
}

const msgTimer: any = {}

function sendMsg(socket: any, eventName: string, data: any) {
  return new Promise((resolve, reject) => {
    console.log("sendmsg", eventName);

    if (!socket || !socket.connected) {
      reject("socket don't connected");
      return;
    }

    let timerName = eventName

    if (eventName === 'EMIT_MOUSE') {
      timerName += '_' + data.type
    }

    if (msgTimer[timerName]) {
      window.clearTimeout(msgTimer[timerName])
      msgTimer[timerName] = undefined
    }

    msgTimer[timerName] = window.setTimeout(() => {
      socket.timeout(1000).emit(eventName, data, (err: any, res: any) => {
        msgTimer[timerName] = undefined

        if (err) {
          reject(err);
          return;
        }
  
        resolve(res);
      });
    }, 100)
  });
}

export default (props: PropsWithChildren<IProps>): JSX.Element => {
  const screenRef = useRef<HTMLDivElement>(null);
  const [clients, setClients] = useState<Array<any>>([]);
  const mouse = useMouse(() => screenRef.current);
  const socketRef = useRef<any>(null);

  const fullImage = useMemo(() => {
    if (clients.length > 0) {
      return clients.find(item => item.imageEnable)?.image;
    }

    return null;
  }, [clients]);

  const allMounseEnable = useMemo(() => {
    return clients.every((item) => item.mouseEnable);
  }, [clients]);

  const allKeyboardEnable = useMemo(() => {
    return clients.every((item) => item.keyboardEnable);
  }, [clients]);

  const imageEnable = useMemo(() => {
    return clients.some((item) => item.imageEnable);
  }, [clients]);

  const sendMsgWithSocket = (eventName: string, data: any) => {
    if (!socketRef.current) {
      return;
    }

    return sendMsg(socketRef.current, eventName, data);
  };

  const onScreenMouseEvent: (type: string) => React.MouseEventHandler<HTMLDivElement> = type => (e) => {
    if (!screenRef.current || !socketRef.current) {
      return;
    }

    const elementWidth = screenRef.current.clientWidth;
    const elementHeight = screenRef.current.clientHeight;

    const { elementX, elementY } = mouse;
    e.preventDefault();

    const sendData = {
      type,
      right: e.type === "contextmenu",
      x: elementX / elementWidth,
      y: elementY / elementHeight,
    };

    sendMsgWithSocket("EMIT_MOUSE", sendData);
  }

  useEventListener(
    "keydown",
    (ev) => {
      ev.preventDefault();

      const command = [];
      let sendKey = ev.key;

      if (['1', '2', '3', '4', '5'].includes(sendKey)) {
        setClients((clients: any) => {
          clients.forEach((item: any, i: number) => {
            item.mouseEnable = i === ((+sendKey) - 1)
          })

          sendAllConfig(clients)
          return clients
        })
      } else if (['`'].includes(sendKey)) {
        setClients((clients: any) => {
          clients.forEach((item: any, i: number) => {
            item.mouseEnable = true
          })

          sendAllConfig(clients)
          return clients
        })
      } else {
        return
      }

      if (sendKey) {
        sendKey = sendKey.toLowerCase();
      }

      if (ev.shiftKey && sendKey !== 'shift') {
        command.push("shift");
      }

      if (ev.ctrlKey && sendKey !== 'control') {
        command.push("control");
      }

      if (ev.altKey && sendKey !== 'alt') {
        command.push("alt");
      }

      const sendData = {
        key: sendKey,
        command,
      };

      sendMsgWithSocket("EMIT_KEYBOARD", sendData);
    },
    {
      target: document,
    }
  );

  useEffect(() => {
    const socket = io("ws://localhost:4000");

    loopCall(() => {
      setClients(
        tmpClients.map((item) => {
          return {
            ...item,
            image: ArrayBufferToBase64(item.image),
          };
        })
      );
    });

    socket.on("connect", () => {
      console.log("client connect");
      socketRef.current = socket;
      sendMsgWithSocket(EMIT_INIT, { type: "SOCKET_TYPE_WEB" });

      socket.on("EMIT_SYNC_CLIENT_IMAGE_INFO", (clients: any[], callback) => {
        tmpClients = clients;

        callback("got it");
      });
    });

    socket.on("close", () => {
      socketRef.current = null;
      console.log("client close");
    });

    socket.connect();
    return () => {
      socket.disconnect();
    };
  }, []);

  const sendAllConfig = (allClients = clients) => {
    const configData = allClients.map((item, j) => {
      return {
        ...item,
        image: undefined,
      };
    });

    sendMsgWithSocket("EMIT_CONFIG", configData);
  };

  

  const onStopImage = () => {
    clients.forEach((item) => {
      item.imageEnable = false;
    });

    sendAllConfig();
  };

  const onThumpClick = (item: any, i: number) => (ev: any) => {
    clients.forEach((item, j) => {
      item.imageEnable = i === j;
    });

    sendAllConfig();
  };

  const onMouseEnable = (item: any, i = -1) => (ev: any) => {
    console.log("onMouseEnable", ev);

    if (item === 'all') {
      clients.forEach(item => {
        item.mouseEnable = ev
      })
    } else {
      item.mouseEnable = ev;
    }
    
    sendAllConfig();
  };

  const onKeyboardEnable = (item: any, i = -1) => (ev: any) => {
    console.log("onKeyboardEnable", ev);

    if (item === 'all') {
      clients.forEach(item => {
        item.keyboardEnable = ev
      })
    } else {
      item.keyboardEnable = ev;
    }
    sendAllConfig();
  };

  return (
    <div className="flex flex-row w-full h-full select-none">
      <div className="w-[180px] flex flex-col h-full border bg-slate-500">
        <div className="flex flex-col justify-start px-4 py-2 gap-1">
          <div className="font-bold">全部设置：</div>
          <div className="flex flex-row items-center gap-2 text-slate-800">
            <span>鼠标监听</span>
            <Switch value={allMounseEnable} onClick={onMouseEnable('all')} />
          </div>
          <div className="flex flex-row items-center gap-2 text-slate-800">
            <span>键盘监听</span>
            <Switch value={allKeyboardEnable} onClick={onKeyboardEnable('all')} />
          </div>
          <div className="flex flex-row items-center gap-2 text-slate-800">
            <span>图像开启</span>
            <Switch value={imageEnable} onClick={onStopImage} />
          </div>
          <div className="mt-4 font-bold">单独设置：</div>
          {clients.map((item: any, i) => {
            return (
              <div key={item.key} className="w-full border px-1 py-2">
                <div>{i + 1}号：</div>
                <div className="flex flex-col items-center justify-start">
                  <div className="flex flex-row items-center gap-2 text-slate-800">
                    <span>鼠标监听</span>
                    <Switch
                      value={item.mouseEnable}
                      onClick={onMouseEnable(item, i)}
                    />
                  </div>
                  <div className="flex flex-row items-center gap-2 text-slate-800">
                    <span>键盘监听</span>
                    <Switch
                      value={item.keyboardEnable}
                      onClick={onKeyboardEnable(item, i)}
                    />
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
      <div className="flex-1 flex flex-col h-full">
        <div className="flex flex-row overflow-auto w-full h-[200px] border">
          {clients.map((item: any, i) => {
            return (
              <div
                onClick={onThumpClick(item, i)}
                key={item.key}
                className="w-[200px] max-h-full relative border"
              >
                <div className="absolute left-0 top-0 right-0 bg-slate-300">
                  {i + 1}号
                </div>
                {item.image && (
                  <img
                    src={`data:image/png;base64,${item.image}`}
                    className="w-full h-full"
                  />
                )}
              </div>
            );
          })}
        </div>
        <div
          ref={screenRef}
          onContextMenu={onScreenMouseEvent('click')}
          onClick={onScreenMouseEvent('click')}
          onMouseDown={onScreenMouseEvent('down')}
          onMouseUp={onScreenMouseEvent('up')}
          onMouseMove={onScreenMouseEvent('move')}
          className="flex-1 min-h-0 w-full"
        >
          {fullImage && (
            <img
              src={`data:image/png;base64,${fullImage}`}
              className="w-full h-full pointer-events-none"
            />
          )}
        </div>
      </div>
    </div>
  );
};
